import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;


public class ReadMNIST {

	private static ReadMNIST instance;
	
	private File dataFile;
	private File labelFile;
	private int offset;
	
	private ReadMNIST() {
		offset = 0;
	}
	
	public static ReadMNIST getInstance() {
		if (instance == null)
			instance = new ReadMNIST();
		return instance;
	}
	
	public void setDataFile(String filename) {
		dataFile = new File(filename);
	}
	
	public void setLabelFile(String filename) {
		labelFile = new File(filename);
	}
	
	public void setFiles(String dataFilename, String labelFilename) {
		dataFile = new File(dataFilename);
		labelFile = new File(labelFilename);
	}
	
	public int[] readLabels() throws IOException {
		if (dataFile == null)
			return null;
		FileInputStream stream = new FileInputStream(labelFile);
		
		stream.read();
		stream.read();
		int dataType = stream.read();
		
		switch (dataType) {
		case 0x08:
			return readUByteLabel(stream);
		case 0x09:
			stream.close();
			return null;
		case 0x0B:
			stream.close();
			return null;
		case 0x0C:
			stream.close();
			return null;
		case 0x0D:
			stream.close();
			return null;
		case 0x0E:
			stream.close();
			return null;
		default:
			stream.close();
			return null;
		}
	}
	
	public int[][] getNextImage() throws IOException{
		if (dataFile == null)
			return null;
		FileInputStream stream = new FileInputStream(dataFile);
		stream.read();
		stream.read();
		stream.read();
		
		byte[] intSize = new byte[4]; 
		stream.read();
		stream.read(intSize);
		
		stream.read(intSize);
		int rows = (char)(intSize[0] << 24) | (char)(intSize[1] << 16) | (char)(intSize[2] << 8) | (char)(intSize[3]) ;;
		
		stream.read(intSize);
		int cols = (char)(intSize[0] << 24) | (char)(intSize[1] << 16) | (char)(intSize[2] << 8) | (char)(intSize[3]) ;;
		
		cols--;
		stream.skip(offset);
//		for(int i=0; i!=offset; i++) {
//			stream.read();
//		}
		
		int[][] image = new int[rows][cols+1];
		int b = 0;
		b = stream.read();
		int row = 0;
		int col = 0;
		for (int i=0; i!= rows*cols; i++) {
			if (row < rows) {
				if (col < cols) {
					image[row][col] = b;
					col++;
				}
				else {
					col = 0;
					row++;
				}
				b = stream.read();
			}
			else {
				row = 0;
			}
		}
		stream.close();
		offset += rows*(cols+1);
		
		return image;
	}

	public int[][][] readImageFile(String filename, int[] labels, int max) throws IOException {
		FileInputStream stream = null;
		File fileHandle = new File(filename);
		stream = new FileInputStream(fileHandle);
		
		stream.read();
		stream.read();
		int dataType = stream.read();
		
		switch (dataType) {
		case 0x08:
			return readUByteImage(stream, max);
		case 0x09:
			stream.close();
			return null;
		case 0x0B:
			stream.close();
			return null;
		case 0x0C:
			stream.close();
			return null;
		case 0x0D:
			stream.close();
			return null;
		case 0x0E:
			stream.close();
			return null;
		default:
			stream.close();
			return null;
		}
	}
	
	private int[][][] readUByteImage(FileInputStream fileIn, int max) throws IOException {
		int[][][] array = null;
		byte[] intSize = new byte[4]; 
		fileIn.read();
		fileIn.read(intSize);
		int itemCount = (char)(intSize[0] << 24) | (char)(intSize[1] << 16) | (char)(intSize[2] << 8) | (char)(intSize[3]) ;
		
		fileIn.read(intSize);
		int rows = (char)(intSize[0] << 24) | (char)(intSize[1] << 16) | (char)(intSize[2] << 8) | (char)(intSize[3]) ;;
		
		fileIn.read(intSize);
		int cols = (char)(intSize[0] << 24) | (char)(intSize[1] << 16) | (char)(intSize[2] << 8) | (char)(intSize[3]) ;;
		
		cols--;
		array = new int[itemCount][rows][cols+1];
		int itemNum = 0;
		int b = 0;
		b = fileIn.read();
		int row = 0;
		int col = 0;
//		String folderName;
//		if (itemNum < labels.length)
//			folderName = labels[itemNum] + "/";
//		else
//			folderName = "unknown/";
//		File folder = new File("out/"+folderName+"/");
//		if (!folder.exists()) folder.mkdir();
//		File outFile = new File("out/"+folderName+itemNum+".txt");
//		if (!outFile.exists()) outFile.createNewFile();
//		FileOutputStream out = new FileOutputStream(outFile);
		int dots = 0;
		while(b > -1 && itemNum < itemCount && itemNum < max) {
			if (row < rows) {
				if (col < cols) {
					array[itemNum][row][col] = b;
//					System.out.print((b > 0 ? (b > 64 ? (b > 128 ? "X" : "x") : ".") : " "));
//					char c = (b > 0 ? '1' : '0');
//					out.write(c);
					col++;
				}
				else {
					col = 0;
					row++;
//					System.out.println();
//					out.write('\n');
				}
				b = fileIn.read();
			}
			else {
				row = 0;
				itemNum++;
//				out.flush();
//				if (itemNum < labels.length)
//					folderName = labels[itemNum] + "/";
//				else
//					folderName = "unknown/";
//				folder = new File("out/"+folderName+"/");
//				if (!folder.exists()) folder.mkdir();
//				outFile = new File("out/"+folderName+itemNum+".txt");
//				if (!outFile.exists()) outFile.createNewFile();
//				out = new FileOutputStream(outFile);
//				System.out.println();
				if ((float)itemNum / max >= (float)dots / 10) {
					System.out.print("-");
					dots++;
				}
			}
		}
		System.out.println();
		fileIn.close();
//		out.close();
		
		return array;
	}
	
	public int[] readLabelFile(String filename) throws IOException {
		
		File fileHandle = new File(filename);
		FileInputStream stream = null;
		try {
			stream = new FileInputStream(fileHandle);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		if (stream == null) {
			return null;
		}
		
		stream.read();
		stream.read();
		int dataType = stream.read();
		
		switch (dataType) {
		case 0x08:
			return readUByteLabel(stream);
		case 0x09:
			stream.close();
			return null;
		case 0x0B:
			stream.close();
			return null;
		case 0x0C:
			stream.close();
			return null;
		case 0x0D:
			stream.close();
			return null;
		case 0x0E:
			stream.close();
			return null;
		default:
			stream.close();
			return null;
		}
	}
	
	private int[] readUByteLabel(FileInputStream fileIn) throws IOException {
		int[] array = null;
		byte[] intSize = new byte[4]; 
		fileIn.read();
		fileIn.read(intSize);
		int itemCount = (char)(intSize[0] << 24) | (char)(intSize[1] << 16) | (char)(intSize[2] << 8) | (char)(intSize[3]) ;
		array = new int[itemCount];
		int itemNum = 0;
		int b = fileIn.read();
		while(b > -1 && itemNum < itemCount) {
			array[itemNum] = b;
			itemNum++;
			b = fileIn.read();	
		}
		fileIn.close();
		
		return array;
	}
}

